<template>
  <div class="page page-screenshare-webrtc">
    <h4 class="text-center">屏幕共享服务端（WebRTC）</h4>
    <form class="page-body row">
      <div class="col-4">
        <fieldset>
          <legend>窗口列表&操作</legend>
          <div class="fieldset-content">
            <div class="form-group row-flex" align="center">
              <label class="row-flex-prefix">本机IP</label>
              <div class="row-flex-main">
                <textarea class="form-control" v-model="ipsList" rows="2" readonly></textarea>
              </div>
            </div>
            <div class="form-group row-flex" align="center">
              <label class="row-flex-prefix">接收端</label>
              <div class="row-flex-main">
                <input class="form-control" type="text" v-model.trim="clientUrl" readonly />
              </div>
            </div>
            <div class="form-group row-flex" align="center">
              <div class="row-flex-main text-left">请选择要共享的窗口</div>
              <div class="row-flex-suffix">
                <i class="fa fa-refresh" title="刷新" @click="getScreensList"></i>
              </div>
            </div>
            <div class="form-group screens-list">
              <div v-for="(v,i) in screensList" :key="i"
                :class="['screen-item', {active: i === screenActiveIndex}]"
                @click="screenItemClick(i)"
              >
                <img :src="v.thumbnail.toDataURL()">
              </div>
            </div>
            <div class="row">
              <div class="col-6"><x-button block @click="createSubWindow">接收端演示窗口</x-button></div>
              <div class="col-6"><x-button block @click="downloadClient">导出接收端文件</x-button></div>
            </div>
            <div class="row">
              <div class="col-6"><x-button block @click="sendStream">开始推流</x-button></div>
              <div class="col-6"><x-button block @click="stopStream">停止推流</x-button></div>
            </div>
          </div>
        </fieldset>
      </div>
      <div class="col-8">
        <fieldset>
          <legend>直播画面</legend>
          <div class="fieldset-content">
            <video ref="video" autoplay="autoplay"></video>
          </div>
        </fieldset>
      </div>
    </form>
  </div>
</template>

<script>
import io from 'socket.io-client'
const {ipcRenderer, remote} = global.require('electron')
const fs = global.require('fs')
const path = global.require('path')
let webrtcServer = null
let webrtcClientZip = path.join(remote.app.getAppPath(), '../webrtc-client/webrtcClient.zip')

export default {
  name: 'screenshare-webrtc',
  data () {
    return {
      wsWaiter: null,
      myStream: null,
      ipsList: this.$sharedObject('ips').join('\n'),
      clientUrl: 'http://[服务端IP]:7650/static/index.html',
      screensList: [],
      screenActiveIndex: -1,
      connectionsDict: {}
    }
  },
  mounted () {
    this.$nextTick(() => {
      webrtcServer = this.$sharedObject('webrtcServer')
      this.getScreensList()
      this.startWebrtcServer()
      this.listenWebrtcServer()
    })
  },
  beforeDestroy () {
    this.closeWebrtcServer()
  },
  methods: {
    // 获取窗口列表
    getScreensList () {
      window.electron.desktopCapturer.getSources({types: ['window', 'screen']})
      .then((sources) => {
        this.screensList = sources || []
      })
    },
    // 获取指定窗口的镜像流数据
    getStreamBySource (source) {
      return navigator.mediaDevices.getUserMedia({
        audio: false,
        video: {
          mandatory: {
            chromeMediaSource: 'desktop',
            chromeMediaSourceId: source.id
          }
        }
      }).then((stream) => {
        return stream
      }).catch((error) => {
        if (error.name === 'NotReadableError') {
          this.stopStream()
          this.screenActiveIndex = -1
          this.getScreensList()
        } else {
          console.log(error)
        }
      })
    },
    // 获取本程序窗口的镜像流数据
    getStreamSelf () {
      let source = this.screensList.find(item => item.name === document.title)
      return this.getStreamBySource(source)
    },
    // 启动webrtc websocket服务器
    startWebrtcServer () {
      webrtcServer.start()
    },
    // 关闭webrtc websocket服务器
    closeWebrtcServer () {
      webrtcServer.stop()
    },
    // 连接webrtc websocket服务器
    listenWebrtcServer () {
      let _this = this
      _this.wsWaiter = io.connect(`http://127.0.0.1:7650/servers`)
      _this.wsWaiter.on('remoteCandidate', (res) => {
        console.log('remoteCandidate:', res)
        let {id, data} = res || {}
        if (!data || !_this.myStream) return
        if (_this.connectionsDict[id]) {
          _this.connectionsDict[id].addIceCandidate(data)
        }
      })
      _this.wsWaiter.on('remoteDescription', (res) => {
        console.log('remoteDescription:', res)
        let {id, data} = res || {}
        if (!data || !_this.myStream) return
        if (_this.connectionsDict[id]) {
          _this.connectionsDict[id].setRemoteDescription(data)
        }
      })
      _this.wsWaiter.on('remoteJoin', (res) => {
        console.log('remoteJoin:', res)
        let {id} = res || {}
        _this.createPeerConnection({id, stream: _this.myStream}).then((conn) => {
          _this.connectionsDict[id] = conn
          _this.createOffer(id)
          _this.wsWaiter && _this.wsWaiter.emit('canJoin', {id})
        })
      })
      _this.wsWaiter.on('remoteLeave', (res) => {
        console.log('remoteLeave:', res)
        let {id} = res || {}
        if (_this.connectionsDict[id]) {
          _this.connectionsDict[id].close()
          delete _this.connectionsDict[id]
        }
      })
    },
    // 创建连接
    createPeerConnection ({id, stream}) {
      return new Promise((resolve, reject) => {
        console.log('createPeerConnection:', id, stream)
        if (!id || !stream) reject('没有连接对象或视频流')
        let peerConnection = new RTCPeerConnection()
        peerConnection.onicecandidate = (e) => {
          if (!e.candidate) return
          this.wsWaiter && this.wsWaiter.emit('candidate', {id, data: e.candidate})
        }
        stream.getTracks().forEach(track => {
          peerConnection.addTrack(track, stream)
        })
        resolve(peerConnection)
      })
    },
    createOffer (id) {
      if (!this.connectionsDict[id]) {
        return Promise.reject()
      } else {
        return this.connectionsDict[id].createOffer({
          offerToRecieveAudio: 0,
          offerToRecieveVideo: 1,
        }).then((data) => {
          this.connectionsDict[id].setLocalDescription(data)
          this.wsWaiter && this.wsWaiter.emit('description', {id, data})
        }).catch((err) => {
          console.error('createOffer:', err)
          this.$toast('createOffer 时发生错误')
        })
      }
    },
    // 加载视频流
    loadStream (stream) {
      if (!this.$refs.video) return
      try {
        this.$refs.video.srcObject = stream
      } catch (err) {
        this.$refs.video.src = window.URL.createObjectURL(stream)
      }
    },
    // 开始推流
    sendStream () {
      let screenActive = this.screensList[this.screenActiveIndex]
      if (!screenActive) return
      this.getStreamBySource(screenActive).then((stream) => {
        this.loadStream(stream)
        this.myStream = stream
        let clientIds = Object.keys(this.connectionsDict)
        if (clientIds.length && this.wsWaiter) {
          this.wsWaiter.emit('resetConnection', {ids: clientIds})
        }
      })
    },
    // 停止推流
    stopStream () {
      this.myStream = null
      this.loadStream(null)
      let ids = []
      for (let key in this.connectionsDict) {
        ids.push(key)
        this.connectionsDict[key] && this.connectionsDict[key].close()
        delete this.connectionsDict[key]
      }
      this.wsWaiter && this.wsWaiter.emit('closeConnection', {ids})
    },
    // 窗口Item被点击
    screenItemClick (index) {
      if (this.screenActiveIndex === index) return
      this.screenActiveIndex = index
    },
    // 创建子窗口
    createSubWindow () {
      ipcRenderer.send('createSubWindow', {path: 'screenshare-webrtc-client'})
    },
    // 导出接收端
    downloadClient () {
      if (!fs.existsSync(webrtcClientZip)) {
        this.$toast('导出失败，未找到可用文件')
        return
      }
      let filePath = path.join(this.$sharedObject('desktopPath'), 'webrtcClient.zip')
      try {
        fs.copyFileSync(webrtcClientZip, filePath)
        this.$toast(`接收端已经导出到${this.$sharedObject('desktopPath')}`)
      } catch (err) {
        this.$toast('导出失败:' + err)
      }
    }
  }
}
</script>

<style scoped lang="less">
@import "~@/assets/css/_mixins.less";
@import "~@/assets/css/_color.less";
@gapSize: 5px;

.page-screenshare-webrtc {
  display: flex;
  flex-direction: column;
  .page-body {
    height: 0;
    flex-grow: 1;
    fieldset {
      height: 100%;
      .fieldset-content {
        height: calc(100% - 2em);
        display: flex;
        flex-direction: column;
        position: relative;
      }
      .row {margin-top: .5em;}
      .fa-refresh {
        padding: .25em .5em;
        cursor: pointer;
      }
      .screens-list {
        height: 0;
        flex-grow: 1;
        overflow-y: auto;
        .screen-item {
          display: inline-block;
          width: 50%;
          padding: @gapSize;
          img {
            display: block;
            width: 100%;
            height: 60%;
            object-fit: contain;
            background-color: rgba(0,0,0,.1);
          }
          &.active {
            img {outline: 2px solid @colorWarning;}
          }
        }
      }
      video {
        .absolute-fullscreen;
        width: 100%;
        height: 100%;
        object-fit: contain;
        background-color: #333;
      }
    }
  }
}
</style>
